#include <sourcemod>
#include <sdktools>
#include <adminmenu>
#include <l4d_stocks>

#include "macros.sp"
#include "helpers.sp"

const TEAM_NONE = 0;

// SDK call handles
new Handle:gConf = INVALID_HANDLE;
new Handle:sdkSetPlayerSpec = INVALID_HANDLE;
new Handle:sdkTakeOverBot = INVALID_HANDLE;
new Handle:timer_replace_team = INVALID_HANDLE;

new bool:team_spectator[MAXCLIENTS] = {false, ...};
new bool:logical_team[2][MAXCLIENTS];
new bool:can_switch_team = false;

new String:ProperTeamNames[][] = {
	"None",
	"Spectator",
	"Survivor",
	"Infected"
};


public OnPluginStart()
{
	gConf = LoadGameConfigFile("l4dteamspanel");
	if(gConf == INVALID_HANDLE)
	{
		ThrowError("Could not load gamedata/l4dteamspanel.txt");
	}

	StartPrepSDKCall(SDKCall_Player);
	PrepSDKCall_SetFromConf(gConf, SDKConf_Signature, "SetHumanSpec");
	PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);
	sdkSetPlayerSpec = EndPrepSDKCall();
	
	StartPrepSDKCall(SDKCall_Player);
	PrepSDKCall_SetFromConf(gConf, SDKConf_Signature, "TakeOverBot");
	PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
	sdkTakeOverBot = EndPrepSDKCall();

	RegConsoleCmd("teams", show_team_menu);
	RegConsoleCmd("afk", go_afk);
	RegConsoleCmd("away", go_afk);
	
	RegAdminCmd("spec", move_spec, ADMFLAG_KICK);
	RegAdminCmd("swap", swap_team, ADMFLAG_KICK);

	HookEvent("round_start", Event_RoundStart, EventHookMode_Post);
	HookEvent("round_end", Event_RoundEnd);
	HookEvent("versus_round_start", Event_VersusRoundStart);
	HookEvent("player_disconnect", Event_PlayerDisconnect);
	
	for(new i = 0; i < MAXCLIENTS; i++)
		clean_player(i);
}


public Action:go_afk(client, args)
{
	PerformSwitch(client, TEAM_SPECTATORS, true);
	save_player_team(client);
}

public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
	new id = GetClientOfUserId(GetEventInt(event, "userid"));
	clean_player(id);
}

public Action:Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{

	if(is_first_round())
	{
		timer_replace_team = CreateTimer(2.0, replace_team, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
	}

	can_switch_team = false;

}

public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
	if(timer_replace_team != INVALID_HANDLE)
	{
		KillTimer(timer_replace_team);
		timer_replace_team = INVALID_HANDLE;
	}

	can_switch_team = false;
	save_team();
}

public Event_VersusRoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
	if(timer_replace_team != INVALID_HANDLE)
	{
		KillTimer(timer_replace_team);
		timer_replace_team = INVALID_HANDLE;
	}
	can_switch_team = true;
}

/* put back afker in the spec team*/
public Action:replace_team(Handle:Timer, any:junk)
{
	FOR_EACH_HUMAN(i)
	{		
		if(is_in_right_team(i) == false)
		{
			ChangeClientTeam(i, TEAM_SPECTATORS);
		}
	}

	FOR_EACH_HUMAN_SPECTATOR(client)
	{
		switch_in_right_team(client);
	}
}

switch_in_right_team(client)
{

	if(was_spectator(client))
		return;
	
	if(logical_team[0][client])
	{		
		PerformSwitch(client, get_real_team(0), true);
	}
	else if(logical_team[1][client])
	{
		PerformSwitch(client, get_real_team(1), true);
	}
}
bool:is_in_right_team(client)
{

	new team = GetClientTeam(client);

	//if was spec, it's ok
	if(was_spectator(client))
	{
		return false;
	}

	if(team == TEAM_NONE)
	{
		return true;
	}

	new logical_team_index = get_logical_team(team);

	if(logical_team_index == -1) //no team
	{
		return true;
	}

	//sub_debug("Client %d - REALTEAM : %d - LOGICAL TEAM : %d",client, team, logical_team_index);

	//if was not connected on last round
	if(logical_team[0][client] == false && logical_team[1][client] == false)
	{
		return true;
	}

	if(logical_team[logical_team_index][client])
	{
		return true;
	}
	else
	{
		return false;
	}
}

clean_player(client)
{
	if(IsValidClientIndex(client))
	{
		logical_team[0][client] = false;
		logical_team[1][client] = false;
		team_spectator[client] = false;
	}
}

save_player_team(client)
{
	//update team
	new team = GetClientTeam(client);
	new index = get_logical_team(team);
	clean_player(client);

	if(team != TEAM_SPECTATORS)
	{
		logical_team[index][client] = true;
	}
	else
	{
		team_spectator[client] = true;
	}

}

save_team()
{
	for(new i = 0; i < MAXCLIENTS; i++)
	{
		clean_player(i)
	}

	/* If teams are flipped, surv is team 1, inf is team 0, else surv is team 0 and inf is team 1*/
	new survindex = get_logical_team(TEAM_SURVIVORS);
	new infindex = get_logical_team(TEAM_INFECTED);

	FOR_EACH_SURVIVOR_HUMAN(client)
	{
		logical_team[survindex][client] = true;
	}

	FOR_EACH_INFECTED_HUMAN(client)
	{
		logical_team[infindex][client] = true;
	}

	FOR_EACH_HUMAN_SPECTATOR(client)
	{
		team_spectator[client] = true;	
	}
}


bool:was_spectator(client)
{
	// Check if player is still valid ...
	if (!IsValidClient(client))
	{
  		return false;
	}

	return team_spectator[client];
}

public Action:move_spec(client, args)
{
	decl String:full[128];
	GetCmdArgString(full, sizeof(full));
	decl String:name[128];
	decl target_list[MAXCLIENTS], bool:tn_is_ml;

	if(IsValidClient(client))
	{
		
		new target_count = ProcessTargetString(
			full,
			client,
			target_list,
			MAXCLIENTS,
			COMMAND_FILTER_NO_BOTS,
			name,
			sizeof(name),
			tn_is_ml);
		
		if(!tn_is_ml && target_count == 1)
		{
			
			PerformSwitch(target_list[0], TEAM_SPECTATORS, true);
			PrintToChatAllExclude(target_list[0], -1, "\x03%N\x01 was moved to spectator.", target_list[0]);
			PrintToChat(target_list[0], "\x05You were moved to spectator by an admin.\x01");
			save_player_team(target_list[0]);
		}
	}

	return Plugin_Handled;

}

public Action:show_team_menu(client, args)
{

	if(!IsValidClient(client))
		return;	

	new String:surv_count[32];
	new String:inf_count[32];
	new String:spec_count[32];

	new total_surv = get_total_client(TEAM_SURVIVORS);
	new total_inf = get_total_client(TEAM_INFECTED);
	new total_spec = get_total_client(TEAM_SPECTATORS);

	Format(surv_count, sizeof(surv_count), "Survivors (%d/10)", total_surv);
	Format(inf_count, sizeof(inf_count), "Infected (%d/10)", total_inf);
	Format(spec_count, sizeof(spec_count), "Specators (%d)", total_spec);

	new Handle:panel = CreatePanel();
	SetPanelTitle(panel, "Select a team :");
	DrawPanelItem(panel, surv_count);
	DrawPanelItem(panel, inf_count);
	DrawPanelItem(panel, spec_count);

	if(panel != INVALID_HANDLE)
	{
		SendPanelToClient(panel, client, panel_handler, 20);

		CloseHandle(panel);
	}
}


public panel_handler(Handle:menu, MenuAction:action, client, choice)
{
	if (action == MenuAction_Select)
	{
		new team;
		if(choice == 1)
		{
			team = 2;
		}
		else if(choice == 2)
		{
			team = 3;
		}
		else
		{
		        team = 1;
		}

		PerformSwitch(client, team, false);
	}	
}

get_total_client(team)
{
	new count = 0;
	FOR_EACH_HUMAN(client)
	{
		if(GetClientTeam(client) == team || team == TEAM_NONE)
			count++;
	}

	return count;
}


PerformSwitch(target, team, bool:auto) {

	// Check if player is still valid ...
	if (!IsValidClient(target))
	{
		return false;
	}
	
	// If teams are the same ...
	if (GetClientTeam(target) == team)
	{
		return false;
	}

	if(!auto && can_switch_team == false && team != TEAM_SPECTATORS)
	{
		PrintToChat(target, "\x05Wait round start before change team.");
		return false;
	}
	
	// We check if target team is full...
	if (IsTeamFull(team))
	{	
		PrintToChat(target, "\x05%s\x01 team is full", ProperTeamNames[team]);
		return false;
	}
	
	// If player was on infected .... 
	if (GetClientTeam(target) == TEAM_INFECTED)
	{		
		// ... and he wasn't a tank nor ghost...
		new String:iClass[100];
		
		GetClientModel(target, iClass, sizeof(iClass));
		
		if (StrContains(iClass, "hulk", false) == -1 && !L4D_IsPlayerGhost(target)) 
		{ 
			ForcePlayerSuicide(target); 
		}
	}
	
	// If target is survivors
	if (team == TEAM_SURVIVORS)
	{


		// first we switch to spectators ..
		ChangeClientTeam(target, TEAM_SPECTATORS); 
		
		FOR_EACH_ALIVE_SURVIVOR_BOT(bot)
		{
			// force player to spec humans
			SDKCall(sdkSetPlayerSpec, bot, target); 
			
			// force player to take over bot
			SDKCall(sdkTakeOverBot, target, true); 
			PrintToChatAll("\x03%N\x01 joined the \x03%s\x01 team!", target, ProperTeamNames[team]);
			return true;
		}

		//if don't find alive, take a dead one
		FOR_EACH_DEAD_SURVIVOR_BOT(bot)
		{
			// force player to spec humans
			SDKCall(sdkSetPlayerSpec, bot, target); 
			
			// force player to take over bot
			SDKCall(sdkTakeOverBot, target, true); 
			PrintToChatAll("\x03%N\x01 joined the \x03%s\x01 team!", target, ProperTeamNames[team]);
			return true;
		}
		
	}
	else if(team == TEAM_INFECTED)
	{
		//remove from afk list
		ChangeClientTeam(target, team);
		PrintToChatAll("\x03%N\x01 joined the \x03%s\x01 team!", target, ProperTeamNames[team]);
	}
	else // spectator
	{
		ChangeClientTeam(target, team);
	}
	
	return true;
	
}


bool:IsTeamFull(team) {
	
	if (team == TEAM_SPECTATORS) { return false; }
	
	new count = get_total_client(team);
	
	if (team == TEAM_INFECTED) 
	{ 	
		return (count >= MAXINFECTED);
	}
	else
	{
		return (count >= MAXSURVIVORS);
	}	
}


//player swaping

public Action:swap_team(client, args)
{
	if(IsValidClient(client))
	{
		create_swap_menu(client);
	}
}

create_swap_menu(client)
{	
	new Handle:menu = CreateMenu(handle_menu_swap);
	new String:name[MAX_NAME_LENGTH];
	new String:uid[12];
	FOR_EACH_HUMAN(i)
	{
		if(GetClientTeam(i) == TEAM_SURVIVORS || GetClientTeam(i) == TEAM_INFECTED)
		{
			Format(uid,sizeof(uid),"%i",GetClientUserId(i));
			if(GetClientName(i,name,sizeof(name)))
			{
				AddMenuItem(menu, uid, name);				
			}
		}
	}
	
	SetMenuTitle(menu,"Player To Swap",client);
	if (menu == INVALID_HANDLE)
	{
		LogMessage("Could not create menu for swap");
	}
	else
	{
		DisplayMenu(menu, client, MENU_TIME_FOREVER);
	}
}

public handle_menu_swap(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_Select)
	{
		new String:info[12]; 
		
		if(GetMenuItem(menu, param2, info, sizeof(info)))
		{
			new id = GetClientOfUserId(StringToInt(info));
			if(IsValidHumanSurvivor(id) || IsValidHumanInfected(id))
			{
				if(GetClientTeam(id) == TEAM_SURVIVORS)
				{
					PerformSwitch(id, TEAM_INFECTED, true);
				}
				else if(GetClientTeam(id) == TEAM_INFECTED)
				{
					PerformSwitch(id, TEAM_SURVIVORS, true);
				}
				save_player_team(id);
			}
		}		
	}
	else if (action == MenuAction_End)
	{		
		CloseHandle(menu);
	}
}